package fun.werfamily.wf.auth.core.service.impl;

import fun.werfamily.wf.auth.core.constant.RedisConstant;
import fun.werfamily.wf.auth.core.constant.RedisTimeoutConstants;
import fun.werfamily.wf.auth.core.dto.UserLoginReqDTO;
import fun.werfamily.wf.auth.core.dto.UserTokenDTO;
import fun.werfamily.wf.auth.core.dto.VerifyCodeReqDTO;
import fun.werfamily.wf.auth.core.enums.ResponseCodeEnum;
import fun.werfamily.wf.auth.core.exception.AuthException;
import fun.werfamily.wf.auth.core.feign.client.UserCoreClient;
import fun.werfamily.wf.auth.core.feign.dto.UserInfoDTO;
import fun.werfamily.wf.auth.core.service.AuthService;
import fun.werfamily.wf.auth.core.util.RedisUtil;
import fun.werfamily.wf.base.dto.BaseUserInfoDTO;
import fun.werfamily.wf.base.util.TokenUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * @Author: Mr.WenMing Zhang
 * @Date: 2022/07/26 13:57
 * @Description:
 **/
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {

    @Resource
    private UserCoreClient userCoreClient;

    @Resource
    private RedisUtil redisUtil;

    @Override
    public UserTokenDTO userNameLogin(UserLoginReqDTO userLoginReqDTO) {
        UserTokenDTO userTokenDTO = new UserTokenDTO();
        UserInfoDTO userInfo = userCoreClient.getUserInfo(userLoginReqDTO.getMobile());
        if (null == userInfo) {
            // 用户不存在
            throw new AuthException(ResponseCodeEnum.ACCOUNT_OR_PASSWORD_ERROR);
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(userLoginReqDTO.getPassword(), userInfo.getPassword());
        if (!matches) {
            // 用户名或密码错误
            throw new AuthException(ResponseCodeEnum.ACCOUNT_OR_PASSWORD_ERROR);
        }
        //创建JWT对象
        String token = TokenUtil.genToken(String.valueOf(userInfo.getId()), userInfo.getUserName(), userInfo.getUserCode());
        log.info("token{}", token);
        redisUtil.saveString(RedisConstant.USER_TOKEN + userInfo.getId(), token, RedisTimeoutConstants.CACHE_HOLD_TIME_7DAY);
        userTokenDTO.setToken(token);
        userTokenDTO.setUserInfo(userInfo);
        return userTokenDTO;
    }


    @Override
    public UserTokenDTO verifyLogin(VerifyCodeReqDTO verifyCodeReqDTO) {
        UserTokenDTO userTokenDTO = new UserTokenDTO();
        UserInfoDTO userInfo = userCoreClient.getUserInfo(verifyCodeReqDTO.getMobile());
        if (null == userInfo) {
            // 用户不存在
            throw new AuthException(ResponseCodeEnum.USER_NOT_EXITS);
        }
        if (Boolean.FALSE.equals(userCoreClient.checkVerifyCode(verifyCodeReqDTO))) {
            // 验证码错误
            throw new AuthException(ResponseCodeEnum.VERIFY_CODE_ERROR);
        }
        //创建JWT对象
        String token = TokenUtil.genToken(String.valueOf(userInfo.getId()), userInfo.getUserName(), userInfo.getUserCode());
        log.info("token{}", token);
        redisUtil.saveString(RedisConstant.USER_TOKEN + userInfo.getId(), token, RedisTimeoutConstants.CACHE_HOLD_TIME_7DAY);
        userTokenDTO.setToken(token);
        userTokenDTO.setUserInfo(userInfo);
        return userTokenDTO;
    }

    @Override
    public boolean logout(HttpServletRequest request) {
        BaseUserInfoDTO baseUserInfoDTO = TokenUtil.parseToken(request);
        String tokenKey = RedisConstant.USER_TOKEN + baseUserInfoDTO.getUserId();
        if (Boolean.FALSE.equals(redisUtil.keyExists(tokenKey))) {
            throw new AuthException(ResponseCodeEnum.TOKEN_IS_INVALID);
        }
        String cachedToken = redisUtil.getString(tokenKey);
        String token = TokenUtil.validateToken(request);
        cachedToken = TokenUtil.extractJwtTokenFromAuthorizationHeader(cachedToken);
        if (!StringUtils.equals(cachedToken, token)) {
            log.warn("token 无效 {}", token);
            throw new AuthException(ResponseCodeEnum.TOKEN_IS_INVALID);
        }
        redisUtil.deleteKey(tokenKey);
        return true;
    }
}
